package com.stars.easyms.datasource.interceptor;

import com.stars.easyms.base.util.AnnotationUtil;
import com.stars.easyms.datasource.*;
import com.stars.easyms.datasource.annotation.SpecifyMasterDataSource;
import com.stars.easyms.datasource.annotation.EasyMsRepository;
import com.stars.easyms.datasource.annotation.SpecifySlaveDataSource;
import com.stars.easyms.datasource.common.EasyMsDataSourceConstant;
import com.stars.easyms.datasource.enums.DataSourceType;
import com.stars.easyms.datasource.exception.DataSourceInterceptorException;
import com.stars.easyms.datasource.holder.EasyMsMasterSlaveHolder;
import com.stars.easyms.datasource.holder.EasyMsMasterSlaveDataSourceHolder;
import com.stars.easyms.datasource.mybatisplus.EasyMsMybatisPlusHelper;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * <p>className: EasyMsRepositoryInterceptor</p>
 * <p>description: EasyMsRepository注解的DAO方法拦截器</p>
 *
 * @author guoguifang
 * @version 1.1.0
 * @date 2019-03-03 16:33
 */
public final class EasyMsRepositoryInterceptor extends BaseEasyMsDatasourceMethodInterceptor {

    private EasyMsMultiDataSource easyMsMultiDataSource;

    @Override
    public Object interceptMybatisMethod(MethodInvocation methodInvocation, Class<?> mapperInterface, Method method)
            throws DataSourceInterceptorException {

        // 获取当前方法的类的注解是否有支持的注解类型，若没有则跳过
        if (!hasSupportAnnotationType(mapperInterface) && !EasyMsMybatisPlusHelper.isMybatisPlusMapper(mapperInterface)) {
            return proceed(methodInvocation);
        }

        // 如果有EasyMsRepository注解则使用EasyMsRepository注解的设置
        String datasourceName = EasyMsDataSourceConstant.DEFAULT_DATASOURCE_NAME;
        DataSourceType dataSourceType = DataSourceType.MASTER;
        if (mapperInterface.isAnnotationPresent(EasyMsRepository.class)) {
            EasyMsRepository easyMsRepository = mapperInterface.getAnnotation(EasyMsRepository.class);
            datasourceName = easyMsRepository.datasource();
            dataSourceType = easyMsRepository.query();
        }

        // 判断是否已经确定使用哪个数据源，若已确定则不再重复确定
        boolean isNotConfirmMasterSlaveDataSource = EasyMsMasterSlaveDataSourceHolder.getMasterSlaveDataSource() == null;
        boolean isNotSwitchMasterSlave = EasyMsMasterSlaveHolder.isNotSwitch();

        // 判断使用主数据源还是从数据源
        try {
            if (isNotConfirmMasterSlaveDataSource) {
                EasyMsMasterSlaveDataSourceHolder.putMasterSlaveDataSource(easyMsMultiDataSource.getDataSource(datasourceName));
            }
            if (isNotSwitchMasterSlave) {
                // 优先级排序：方法上注解SpecifyMasterDataSource > 方法上注解SpecifySlaveDataSource > 类上注解EasyMsRepository（Master > Slave）
                if (method.isAnnotationPresent(SpecifyMasterDataSource.class)) {
                    EasyMsMasterSlaveHolder.switchMasterDataSource();
                } else if (method.isAnnotationPresent(SpecifySlaveDataSource.class)) {
                    EasyMsMasterSlaveHolder.switchSlaveDataSource();
                } else if (DataSourceType.MASTER == dataSourceType) {
                    EasyMsMasterSlaveHolder.switchMasterDataSource();
                } else {
                    EasyMsMasterSlaveHolder.switchSlaveDataSource();
                }
            }
            return proceed(methodInvocation);
        } finally {
            if (isNotSwitchMasterSlave) {
                EasyMsMasterSlaveHolder.clear();
            }
            if (isNotConfirmMasterSlaveDataSource) {
                EasyMsMasterSlaveDataSourceHolder.clearMasterSlaveDataSource();
            }
        }
    }

    public EasyMsRepositoryInterceptor(EasyMsMultiDataSource easyMsMultiDataSource) {
        this.easyMsMultiDataSource = easyMsMultiDataSource;
    }

    private boolean hasSupportAnnotationType(Class<?> clazz) {
        for (Class<? extends Annotation> supportAnnotationType : EasyMsDataSourceConstant.SUPPORT_ANNOTATION_TYPE_LIST) {
            if (AnnotationUtil.hasAnnotation(clazz, supportAnnotationType)) {
                return true;
            }
        }
        return false;
    }
}